package org.farng.mp3;

import org.farng.mp3.id3.AbstractID3v2;
import org.farng.mp3.id3.AbstractID3v2Frame;
import org.farng.mp3.id3.ID3v2_4;

import java.io.IOException;
import java.io.RandomAccessFile;

import java.util.Iterator;


/**
 * This class represents any type of tag in an MP3 file, including ID3 and
 * Lyrics and the file name.
 *
 * @author Eric Farng
 * @version $Revision: 1.4 $
 */
public abstract class AbstractMP3Tag extends AbstractMP3FileItem {
    public AbstractMP3Tag() {}

    public AbstractMP3Tag(AbstractMP3Tag copyObject) {
        super(copyObject);
    }

    /**
     * Appends this tag to the given file. Append means any information this
     * tag contains will be added to the file's corresponding tag, but it will
     * not replace any fields that the file already has. If the file does not
     * have this specific tag, a new one will be created.
     *
     * @param file MP3 file to append to.
     *
     * @throws IOException on any I/O error
     * @throws TagException on any exception generated by this library.
     */
    abstract public void append(RandomAccessFile file)
                         throws IOException, TagException;

    /**
     * removes the specific tag the easiest way. <BR> ID3v1 - cuts the length
     * of the tag <BR> lyrics3 -cuts the length of the tag, then writes the
     * id3v1 tag if it existed <BR> id3v2 - just overwrites the ID3 tag
     * indicator at the start of the tag<BR>
     *
     * @param file MP3 file to append to.
     *
     * @throws IOException on any I/O error
     */
    abstract public void delete(RandomAccessFile file)
                         throws IOException;

    /**
     * Overwrites this tag to the given file. Overwrite means any information
     * this tag contains will replace any existing fields in the file's
     * corresponding tag. If the file does not have this specific tag, a new
     * one will be created.
     *
     * @param file MP3 file to overwrite
     *
     * @throws IOException on any I/O error
     * @throws TagException on any exception generated by this library.
     */
    abstract public void overwrite(RandomAccessFile file)
                            throws IOException, TagException;

    /**
     * Looks for this tag. returns true if found. If found, the file pointer is
     * right after the tag start indicator i.e. "TAG" "LYRICSBEGIN" "ID3" + 2
     *
     * @param file MP3 file to overwrite
     *
     * @return returns true if found, false otherwise.
     *
     * @throws IOException on any I/O error
     */
    abstract public boolean seek(RandomAccessFile file)
                          throws IOException;

    /**
     * Returns true if this tag is a subset of the argument. Both tags are
     * converted into ID3v2_4 tags, and then compared frame by frame.
     *
     * @param tag superset tag
     *
     * @return true if this tag is a subset of the argument
     */
    public boolean isSubsetOf(AbstractMP3Tag tag) {
        AbstractID3v2      subset = new ID3v2_4(this);
        AbstractID3v2      superset = new ID3v2_4(tag);
        Iterator           iterator = subset.iterator();
        AbstractID3v2Frame subsetFrame = null;
        AbstractID3v2Frame supersetFrame = null;
        String             identifier = null;

        while (iterator.hasNext()) {
            subsetFrame   = (AbstractID3v2Frame) iterator.next();
            identifier    = subsetFrame.getIdentifier();
            supersetFrame = (AbstractID3v2Frame) superset.getFrame(identifier);

            if (supersetFrame == null) {
                return false;
            }

            if (subsetFrame.isSubsetOf(supersetFrame) == false) {
                return false;
            }
        }

        return true;
    }

    /**
     * This method does nothing, but is called by subclasses for completeness
     *
     * @param tag tag to overwrite
     */
    abstract public void append(AbstractMP3Tag tag);

    /**
     * Determines whether another object is equal to this tag. It just compares
     * if they are the same class, then calls <code>super.equals(obj)</code>.
     *
     * @param obj DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public boolean equals(Object obj) {
        if ((obj instanceof AbstractMP3Tag) == false) {
            return false;
        }

        return super.equals(obj);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    abstract public Iterator iterator();

    /**
     * This method does nothing, but is called by subclasses for completeness
     *
     * @param tag tag to overwrite
     */
    abstract public void overwrite(AbstractMP3Tag tag);

    /**
     * This method does nothing, but is called by subclasses for completeness
     *
     * @param tag tag to write to
     */
    abstract public void write(AbstractMP3Tag tag);
}